/* eslint-disable no-console */
const express = require("express");
const { MongoClient } = require("mongodb");
const SocketServer = require("ws").Server;
const uuid = require("uuid");
const _ = require("lodash");
const path = require("path");
const xlsx = require('node-xlsx');
const stream = require('stream');
const fileUpload = require('express-fileupload');
const bodyParser = require('body-parser');
const { applyOp } = require("./op");

const defaultData = {
    name: "Demo",
    id: uuid.v4(),
    celldata: [{ r: 0, c: 0, v: null }],
    order: 0,
    row: 84,
    column: 60,
    config: {},
    pivotTable: null,
    isPivotTable: false,
    status: 0,
};

const dbName = "fortune-sheet";
const collectionName = "workbook";
const uri = "mongodb://localhost:27017";
const client = new MongoClient(uri);
let presences = [];

async function initMongoDB() {
    await client.connect();
    await client.db("admin").command({ ping: 1 });
    let sheet = await client.db(dbName).collection(collectionName).findOne()
    if (!sheet) {
        console.log('create default sheet')
        await client.db(dbName).collection(collectionName).insertOne(defaultData)
    }
}

initMongoDB();

const router = express.Router();

let broadcastToOthers

async function getData() {
    const db = client.db(dbName);
    const data = await db.collection(collectionName).find().toArray();
    data.forEach((sheet) => {
        if (!_.isUndefined(sheet._id)) delete sheet._id;
    });
    return data;
}



router.use('/', express.static(path.join(__dirname, '../../dist/storybook-static')))

router.use(fileUpload({
    createParentPath: true
}));
router.use(bodyParser.json());
router.use(bodyParser.urlencoded({ extended: true }));

router.post("/upload", async (req, res) => {
    const db = client.db(dbName);
    // console.log(req.body)
    // replace('data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,','')
    let buf = Buffer.from(req.body.file.replace('data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,', ''), 'base64')
    let sheets = xlsx.parse(buf)
    console.log(sheets)
    let collection = await getData()
    sheets.map(async (sheet, i) => {
        let data = {
            name: sheet.name,
            id: uuid.v4(),
            celldata: [{ r: 0, c: 0, v: null }],
            order: collection.length + i,
            row: 84,
            column: 60,
            config: {},
            pivotTable: null,
            isPivotTable: false,
            status: 0,
        };
        for (let i = 0; i < sheet.data.length; i++) {
            const row = sheet.data[i];
            for (let j = 0; j < row.length; j++) {
                const item = row[j];
                data.celldata.push({
                    "r": i,
                    "c": j,
                    "v": {
                        "v": item,
                        "ct": {
                            "fa": "General",
                            "t": (typeof item) == 'number' ? "n" : "g"
                        },
                        "m": item
                    }
                })
            }
        }
        await db.collection(collectionName).insertOne(data)
    })
    res.json({
        ok: true,
        // sheets
    })
});

router.get("/export", async (req, res) => {
    let data = []
    let sheets = await getData()
    let arr = []

    sheets.sort((a, b) => b.order - a.order).map(sheet => {
        let max_row = 0
        let max_col = 0
        if (sheet.celldata) {
            sheet.celldata.map(cell => {
                if (cell.r > max_row) {
                    max_row = cell.r
                }
                if (cell.c > max_col) {
                    max_col = cell.c
                }
            })
            arr = new Array(max_row + 1); //表格有10行
            for (var i = 0; i < arr.length; i++) {
                arr[i] = new Array(max_col + 1).fill(''); //每行有10列
            }
            sheet.celldata.map(cell => {
                arr[cell.r][cell.c] = cell.v ? cell.v.v : ''
            })
        } else {
            arr = []
        }

        data.push({ name: sheet.name, data: arr })

    })
    let buffer = xlsx.build(data)

    // var buffer = Buffer.from(fileData, "base64");

    var readStream = new stream.PassThrough();
    readStream.end(buffer);

    res.set('Content-disposition', 'attachment; filename=' + encodeURI('协作表单.xlsx'));
    res.set('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');

    readStream.pipe(res);
});

// get current workbook data
router.get("/data", async (req, res) => {
    res.json(await getData());
});

router.get("/getCell", async (req, res) => {
    const db = client.db(dbName);
    let sheet = await db.collection(collectionName).findOne({ name: req.query.sheet })
    let value = 0
    // console.log(req.query,data)
    if (sheet && sheet.celldata) {
        let cell = sheet.celldata.filter(cell => cell.r == req.query.r - 1 && cell.c == req.query.c - 1)[0]
        if (cell && cell.v && cell.v.v) {
            value = cell.v.v
        }
    }
    res.json({ value });
});

router.get("/setCell", async (req, res) => {
    const db = client.db(dbName);
    // console.log(req.query)
    let data
    let sheet = await db.collection(collectionName).findOne({ name: req.query.sheet })
    if (!sheet) {
        let collection = await getData()
        data = {
            name: req.query.sheet,
            id: uuid.v4(),
            celldata: [{ r: 0, c: 0, v: null }],
            order: collection.length,
            row: 84,
            column: 60,
            config: {},
            pivotTable: null,
            isPivotTable: false,
            status: 0,
        };
        await db.collection(collectionName).insertOne(data)
        sheet = await db.collection(collectionName).findOne({ name: req.query.sheet })
    }
    let cell = null
    if (sheet && sheet.celldata) {
        cell = sheet.celldata.filter(cell => cell.r == req.query.r - 1 && cell.c == req.query.c - 1)[0]
    } else if (sheet) {
        sheet.celldata = [{ r: 0, c: 0, v: null }]
        // await db.collection(collectionName).updateOne({ name: req.query.sheet }, { celldata: [{ r: 0, c: 0, v: null }] })
    }
    if (cell && cell.v) {
        data = {
            "req": "op",
            "data": [
                {
                    "op": "replace",
                    "value": req.query.value,
                    "path": [
                        "data",
                        req.query.r - 1,
                        req.query.c - 1,
                        "m"
                    ],
                    "id": sheet.id //sheetId
                },
                {
                    "op": "replace",
                    "value": {
                        "fa": "General",
                        "t": (typeof req.query.value) == 'number' ? "n" : "g" // "n" for number "g" for string
                    },
                    "path": [
                        "data",
                        req.query.r - 1,
                        req.query.c - 1,
                        "ct"
                    ],
                    "id": sheet.id //sheetId
                },
                {
                    "op": "replace",
                    "value": req.query.value,
                    "path": [
                        "data",
                        req.query.r - 1,
                        req.query.c - 1,
                        "v"
                    ],
                    "id": sheet.id //sheetId
                },

            ]
        }
    } else {
        if(req.query.r >= 1 && req.query.c >= 1){

        }else{
            res.json({ value: '写入出错, 行列值必须大于等于1' });
            return
        }
        data = {
            "req": "op",
            "data": [
                {
                    "op": "replace",
                    "value": {
                        "v": req.query.value,
                        "ct": {
                            "fa": "General",
                            "t": (typeof req.query.value) == 'number' ? "n" : "g" // "n" for number "g" for string
                        },
                        "m": req.query.value
                    },
                    "path": [
                        "data",
                        req.query.r - 1,
                        req.query.c - 1
                    ],
                    "id": sheet.id //sheetId
                }
            ]
        }
    }

    if (sheet && sheet.celldata) {
        try {
            await applyOp(db.collection(collectionName), data.data);
            broadcastToOthers(null, JSON.stringify(data));
        } catch (error) {
            console.log(error)
            res.json({ value: '写入出错' });
        }
    }
    res.json({ value: req.query.value });
});

// drop current data and initialize a new one
router.get("/init", async (req, res) => {
    const db = client.db(dbName);
    const coll = db.collection(collectionName);
    await coll.deleteMany();
    await db.collection(collectionName).insertOne(defaultData);
    res.json({
        ok: true,
    });
});


const createSocketServer = (server) => {

    const connections = {};

    broadcastToOthers = (selfId, data) => {
        Object.values(connections).forEach((ws) => {
            if (ws.id !== selfId) {
                ws.send(data);
            }
        });
    };

    const wss = new SocketServer({ server, path: "/collaborationSheet" });

    wss.on("connection", (ws) => {
        ws.id = uuid.v4();
        connections[ws.id] = ws;

        ws.on("message", async (data) => {
            const msg = JSON.parse(data.toString());
            if (msg.req === "getData") {
                ws.send(
                    JSON.stringify({
                        req: msg.req,
                        data: await getData(),
                    })
                );
                ws.send(JSON.stringify({ req: "addPresences", data: presences }));
            } else if (msg.req === "op") {
                await applyOp(client.db(dbName).collection(collectionName), msg.data);
                broadcastToOthers(ws.id, data.toString());
            } else if (msg.req === "addPresences") {
                ws.presences = msg.data;
                broadcastToOthers(ws.id, data.toString());
                presences = _.differenceBy(presences, msg.data, (v) =>
                    v.userId == null ? v.username : v.userId
                ).concat(msg.data);
            } else if (msg.req === "removePresences") {
                broadcastToOthers(ws.id, data.toString());
            }
        });

        ws.on("close", () => {
            broadcastToOthers(
                ws.id,
                JSON.stringify({
                    req: "removePresences",
                    data: ws.presences,
                })
            );
            presences = _.differenceBy(presences, ws.presences, (v) =>
                v.userId == null ? v.username : v.userId
            );
            delete connections[ws.id];
        });
    });

}

module.exports = {
    collaborationSheetRouter: router,
    createSocketServer, createSocketServer
} 